const express = require('express');
const jwt = require('jsonwebtoken');
const { expressjwt: expressJwt } = require('express-jwt');
const redis = require('redis'); // 需安装：npm install redis
const app = express();

app.use(express.json());

// ===== 配置 =====
// JWT配置
const JWT_SECRET = process.env.JWT_SECRET || 'your-secure-secret-key'; // 生产环境用环境变量
const JWT_EXPIRES_IN = '1h'; // 令牌有效期1小时
const JWT_EXPIRES_SECONDS = 3600; // 对应秒数，用于Redis过期时间

// Redis客户端配置
const redisClient = redis.createClient({
  url: process.env.REDIS_URL || 'redis://localhost:6379'
});

// 处理Redis连接错误
redisClient.on('error', (err) => {
  console.error('Redis连接错误:', err);
});

// 连接Redis
(async () => {
  await redisClient.connect();
  console.log('Redis连接成功');
})();

// ===== 黑名单操作 =====
/**
 * 将令牌加入黑名单
 * @param {string} jti - 令牌唯一标识
 * @param {number} expiresIn - 过期时间（秒）
 */
const addToBlacklist = async (jti, expiresIn) => {
  try {
    await redisClient.setEx(
      `blacklist:${jti}`, // 键名前缀，避免与其他数据冲突
      expiresIn,
      'true' // 值可任意，仅用于判断存在性
    );
  } catch (err) {
    console.error('添加黑名单失败:', err);
    throw err; // 向上抛出，让调用者处理
  }
};

/**
 * 检查令牌是否在黑名单中
 * @param {string} jti - 令牌唯一标识
 * @returns {boolean} 是否在黑名单中
 */
const isBlacklisted = async (jti) => {
  try {
    const result = await redisClient.get(`blacklist:${jti}`);
    return result === 'true';
  } catch (err) {
    console.error('检查黑名单失败:', err);
    return false; // 失败时默认不视为黑名单（可根据需求调整）
  }
};

// ===== JWT验证中间件 =====
const authenticateJWT = expressJwt({
  secret: JWT_SECRET,
  algorithms: ['HS256'],
  // 验证前检查是否在黑名单
  isRevoked: async (req, token) => {
    if (!token.payload.jti) {
      return true; // 没有jti的令牌直接视为无效
    }
    return await isBlacklisted(token.payload.jti);
  }
}).unless({ path: ['/login'] }); // 排除登录接口

// ===== 路由 =====
// 登录接口 - 生成JWT
app.post('/login', async (req, res) => {
  const { username, password } = req.body;

  // 实际应用中需验证用户名密码（此处简化）
  if (username === 'test' && password === '123456') {
    try {
      // 生成唯一jti（令牌ID）
      const jti = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

      // 生成令牌
      const token = jwt.sign(
        {
          sub: username, // 用户标识（建议用用户ID）
          jti: jti       // 令牌唯一标识
        },
        JWT_SECRET,
        { expiresIn: JWT_EXPIRES_IN }
      );

      return res.json({
        message: '登录成功',
        token: token,
        expiresIn: JWT_EXPIRES_IN
      });
    } catch (err) {
      return res.status(500).json({ message: '生成令牌失败' });
    }
  }

  res.status(401).json({ message: '用户名或密码错误' });
});

// 退出登录接口 - 将令牌加入黑名单
app.post('/logout', authenticateJWT, async (req, res) => {
  try {
    const jti = req.auth.jti;

    if (!jti) {
      return res.status(400).json({ message: '无效的令牌信息' });
    }

    // 将jti加入黑名单，过期时间与令牌有效期一致
    await addToBlacklist(jti, JWT_EXPIRES_SECONDS);

    res.json({ message: '退出登录成功' });
  } catch (err) {
    res.status(500).json({ message: '退出登录失败，请稍后重试' });
  }
});

// 受保护接口示例
app.get('/profile', authenticateJWT, (req, res) => {
  res.json({
    message: '个人信息接口（受保护）',
    user: req.auth.sub,
    tokenId: req.auth.jti
  });
});

// ===== 错误处理 =====
app.use((err, req, res, next) => {
  if (err.name === 'UnauthorizedError') {
    return res.status(401).json({
      message: '令牌无效、已过期或已退出登录，请重新登录'
    });
  }
  console.error('服务器错误:', err);
  res.status(500).json({ message: '服务器内部错误' });
});

// ===== 启动服务器 =====
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

// 优雅关闭
process.on('SIGINT', async () => {
  await redisClient.disconnect();
  process.exit(0);
});
    